/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"
#include "TFile.h"
#include "CDebug.h"

///////////////////////////////////
#if defined(USING_MFC) && defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

int TFile::read		= 1;
int TFile::write	= 2;
int TFile::create	= 4;
int TFile::append	= 8;
int TFile::unicode  = 16;
int TFile::ascii	= 32;
int TFile::text		= 64;
int TFile::binary	= 128;

////////////////////////////////////////////////////////
#if defined(__WINNT__) || defined(__WINCE__)
// We are in windows, let's compile using the windows API's

int TFile::begin   = FILE_BEGIN;
int TFile::end     = FILE_END;
int TFile::current = FILE_CURRENT;

TFile::TFile():m_File(NULL), m_Filename(NULL){}

TFile::~TFile()
{
	Close();
	// Close will delete m_Filename for us
}

int TFile::GetFileType() const
{
	return m_FileType;
}

bool TFile::IsEOF()
{
	if (m_File == NULL) return true;
	else return GetFileSize(m_File, NULL) == SetFilePointer(m_File, 0, NULL, FILE_CURRENT);
}

void TFile::Save(LPCTSTR fileName, TString data)
{
	TFile file;
	if (!file.Open(fileName, TFile::write | TFile::create)) {
		return;
	}

#ifdef UNICODE
	int length = data.GetLength();
	char* buf = new char[length];
	for (int idx = 0; idx < length; ++idx) {
		buf[idx] = (char)(data[idx]);
	}
	file.Write(buf, length);
	delete buf;
#else
	file.Write((LPCTSTR)data, data.GetLength());
#endif
	file.Close();
}

TString TFile::Load(LPCTSTR fileName)
{
	TFile file;
	if (!TFile::Exists(fileName) || !file.Open(fileName, TFile::read)) {
		return _T("");
	}
	int length = file.GetLength();
	char* buf = new char[length + 1];
	file.Read(buf, length);
	buf[length] = 0;
	file.Close();

	TString result = buf;
	delete buf;

	return result;
}

bool TFile::DeleteFile(LPCTSTR filename, bool bForce)
{
	// This static function deletes the specified file
	// NOT this file, you should Close() the file before deleting it
	bool result;

	_TRY {
		if (bForce) {
			// Turn off any possible read-only flag
			::SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
		}
		result = (bool)(::DeleteFile(filename) != 0);
	}
	_CATCH_ALL {
		result = false;
	}
	return result;
}

bool TFile::Move(LPCTSTR fileToMove, LPCTSTR newFileName)
{
	return (bool)(MoveFile(fileToMove, newFileName) != 0);
}

bool TFile::Copy(LPCTSTR fileToCopy, LPCTSTR newFileName)
{
	return (bool)(CopyFile(fileToCopy, newFileName, FALSE) != 0);
}

bool TFile::MakeDir(LPCTSTR dirName)
{
	return (bool)(CreateDirectory(dirName, NULL) != 0);
}

bool TFile::RemoveDir(LPCTSTR dirName)
{
	return (bool)(RemoveDirectory(dirName) != 0);
}

bool TFile::Exists(LPCTSTR filename)
{
	return (bool)(GetFileAttributes(filename) != 0xFFFFFFFF);
}

bool TFile::NewerThan(LPCTSTR first, LPCTSTR second)
{
	// Returns true is first was written to more recently than second

	HANDLE firstFile = CreateFile(
		first,
		GENERIC_READ,			// Read only
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,					// No security attributes
		OPEN_EXISTING,			// We want this to fail if file doesn't exist
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	HANDLE secondFile = CreateFile(
		second,
		GENERIC_READ,			// Read only
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,					// No security attributes
		OPEN_EXISTING,			// We want this to fail if file doesn't exist
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);

	if ((firstFile == INVALID_HANDLE_VALUE) || (secondFile == INVALID_HANDLE_VALUE)) {
		if (firstFile != INVALID_HANDLE_VALUE) CloseHandle(firstFile);
		if (secondFile != INVALID_HANDLE_VALUE) CloseHandle(secondFile);
		InternalError(_T("Compare file times failed, at least one file didn't exist"));
		return false;
	}
	else {
		bool result;
		FILETIME fCreate;
		FILETIME fAccess;
		FILETIME fWrite;
		FILETIME sCreate;
		FILETIME sAccess;
		FILETIME sWrite;

		GetFileTime(firstFile, &fCreate, &fAccess, &fWrite);
		GetFileTime(secondFile, &sCreate, &sAccess, &sWrite);
		result = CompareFileTime(&fWrite, &sWrite) > 0;

		CloseHandle(firstFile);
		CloseHandle(secondFile);

		return result;
	}
}

bool TFile::Open(LPCTSTR lpszFileName, int nOpenMods)
{
	if (m_Filename != NULL) {
		// We need to close the current file before opening another
		Close();
	}

	int CreateNew;
	int ReadWrite = 0;
	if (nOpenMods & read) {
		ReadWrite |= GENERIC_READ;
		CreateNew = OPEN_EXISTING;
	}
	else if (nOpenMods & write) {
		ReadWrite |= GENERIC_WRITE;
		CreateNew = nOpenMods & TFile::create ? CREATE_ALWAYS : OPEN_ALWAYS;
	}
	else if (nOpenMods & append) {
		ReadWrite |= (GENERIC_WRITE | GENERIC_READ);
		CreateNew = OPEN_ALWAYS;
	}
	else {
		// We don't know if it's read of write!
		RunTimeError(_T("Read or write access not specified.  Can't open file: %s"), lpszFileName);
		m_File = NULL;
		return false;
	}

	m_File = CreateFile(
		lpszFileName,
		ReadWrite,
		FILE_SHARE_READ,
		NULL,	// No security attributes
		CreateNew,
		FILE_ATTRIBUTE_NORMAL,
		NULL	// Templage handle
		);

	if (m_File == INVALID_HANDLE_VALUE) {
		// raise new exception
		RunTimeError(_T("Failed to open file: %s"), lpszFileName);
		m_File = NULL;
		return false;
	}

	if (nOpenMods & TFile::append) {
		Seek(0, end);	// Move to end of file
	}

	int lenFilename = _tcslen(lpszFileName);
	m_Filename = new TCHAR[lenFilename + 1];
	_tcscpy(m_Filename, lpszFileName);

	if (!(nOpenMods & (unicode | ascii | text | binary))) {
		nOpenMods |= text;	// If not given, default to text file type
	}

	if (nOpenMods & unicode) {
		// Unicode designator
		m_FileType = unicode;
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & ascii) {
		// ASCII designator
		m_FileType = ascii;
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & text) {
		// Text designator (if reading, try and guess. If writing, use default for system)
		if (nOpenMods & 1) {
			// Reading file
			m_FileType = GuessTextFileType();
		}
		else {
			// Writing file
			#ifdef __WINCE__
				m_FileType = unicode;
			#else
				m_FileType = ascii;
			#endif
		}
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & binary) {
		// Binary mode
		m_FileType = binary;
		m_EOLTerminator.Empty();
	}

	return true;
}

void TFile::Close()
{
	if (m_File != NULL) {
		CloseHandle(m_File);
		m_File = NULL;
	}
	if (m_Filename != NULL) {
		delete[] m_Filename;
		m_Filename = NULL;
	}
}

int TFile::GuessTextFileType()
{
	// Returns either unicode or ascii
	ASSERT(GetPosition() == 0);	// Only call when file is first opened

	if (GetLength() >= 2) {
		char buf[2] = {0, 0};
		Read(buf, 2);
		Seek(0, begin);
		if ((buf[0] == 0) || (buf[1] == 0)) return unicode;
	}
	return ascii;
}

unsigned long TFile::GetLength()
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		return GetFileSize(m_File, NULL);
	}
}

unsigned long TFile::Seek(long offset, int from)
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		ASSERT((from == begin) || (from == current) || (from == end));
		return SetFilePointer(m_File, offset, NULL, from);
	}
}

unsigned long TFile::GetPosition()
{
	if (m_File == NULL) {
		return 0;//0xFFFFFFFF;
	}
	else {
		// Not sure if this is the correct WinAPI way to do this, but...
		return SetFilePointer(m_File, 0, NULL, FILE_CURRENT);
	}
}

unsigned long TFile::Read(void* lpBuf, unsigned long nCount)
{
	if (m_File == NULL) {
		return 0;//0xFFFFFFFF;
	}
	else {
		unsigned long nRead;
		if (!ReadFile(m_File, lpBuf, nCount, &nRead, NULL)) {

		}
		return nRead;
	}
}

unsigned long TFile::Write(const void* lpBuf, unsigned long nCount)
{
	if (m_File == NULL) {
		return 0;//0xFFFFFFFF;
	}
	else {
		unsigned long nWritten;
		if (!WriteFile(m_File, lpBuf, nCount, &nWritten, NULL)) {

		}
		return nWritten;
	}
}

void TFile::WriteLine(TString text)
{
	char* buf;
	int bufLength;
	if (m_FileType == unicode) {
		if (sizeof(TCHAR) == 2) {
			// No conversions needed
			Write((LPCTSTR)text, text.GetLength() * 2);
			WriteEOLTerminator();
			return;
		}
		bufLength = text.GetLength() * 2;
		buf = new char[bufLength];
		for (int idx = 0; idx < text.GetLength(); ++idx) {
			((unsigned short*)buf)[idx] = text[idx];
		}
	}
	else {
		if (sizeof(TCHAR) == 1) {
			// No conversions needed
			Write((LPCTSTR)text, text.GetLength());
			WriteEOLTerminator();
			return;
		}
		bufLength = text.GetLength();
		buf = new char[bufLength];
		for (int idx = 0; idx < text.GetLength(); ++idx) {
			((char*)buf)[idx] = (char)(text[idx]);
		}
	}

	Write(buf, bufLength);
	WriteEOLTerminator();
	delete[] buf;
}

void TFile::WriteEOLTerminator()
{
	char* buf;
	int length = m_EOLTerminator.GetLength();
	if (length > 0) {
		if (m_FileType == unicode) {
			buf = new char[length << 1];
			for (int idx = 0; idx < length; ++idx) {
				((unsigned short*)buf)[idx] = m_EOLTerminator[idx];
			}
			Write(buf, length << 1);
		}
		else {
			buf = new char[length];
			for (int idx = 0; idx < length; ++idx) {
				((char*)buf)[idx] = (char)(m_EOLTerminator[idx]);
			}
			Write(buf, length);
		}
	}
	delete[] buf;
}

int TFile::ReadLine(TString& result)
{
	if (m_FileType == unicode) {
		return ReadUnicodeLine(result);

	}
	else {
		return ReadASCIILine(result);
	}
}

bool TFile::SetEOLTerminator(TString term)
{
	if (term.GetLength() <= 2) {
		m_EOLTerminator = term;
		return true;
	}
	return false;
}

TString TFile::GetEOLTerminator()
{
	return m_EOLTerminator;
}

void TFile::Write(TString text)
{
	int length = text.GetLength();

	if (m_FileType == unicode) {
		if (sizeof(TCHAR) == 2) {
			Write((LPCTSTR)text, length << 1);
		}
		else {
			unsigned short* buf = new unsigned short[length];
			for (int idx = 0; idx < length; ++idx) {
				buf[idx] = text[idx];
			}
			Write(buf, length << 1);
			delete[] buf;
		}
	}
	else {
		if (sizeof(TCHAR) == 1) {
			Write((LPCTSTR)text, length);
		}
		else {
			char* buf = new char[length];
			for (int idx = 0; idx < length; ++idx) {
				buf[idx] = (char)(text[idx]);
			}
			Write(buf, length);
			delete[] buf;
		}
	}
}

int TFile::Read(TString& result, int numChars)
{
	if (m_File == NULL) return 0;

	DWORD bytesRead;
	if (m_FileType == unicode) {
		unsigned short* buf = new unsigned short[numChars + 1];
		ReadFile(m_File, (void*)buf, numChars << 1, &bytesRead, NULL);
		int charsRead = bytesRead == 0 ? 0 : bytesRead >> 1;
		buf[charsRead] = 0;
		result = buf;
		delete[] buf;
		return charsRead;
	}
	else {
#ifdef UNDER_CE
		char* buf = new char[numChars];
		ReadFile(m_File, (void*)buf, numChars, &bytesRead, NULL);
		TCHAR* buf2 = result.GetBuffer(bytesRead);
		//for (DWORD xx = 0; xx < bytesRead; ++xx) {
		//	*buf2++ = (TCHAR)(*buf++);
		//}
		int count = MultiByteToWideChar(CP_ACP, 0, buf, bytesRead, buf2, bytesRead);
		ASSERT(count == (int)bytesRead);
		result.ReleaseBuffer(count);
		delete[] buf;
#else
		char* buf = result.GetBuffer(numChars);
		ReadFile(m_File, (void*)buf, numChars, &bytesRead, NULL);
		result.ReleaseBuffer(bytesRead);
#endif
		return bytesRead;
	}
}

int TFile::ReadASCIILine(TString& result)
{
	char termBuf[2];
	int termLength = m_EOLTerminator.GetLength();
	if (termLength > 0) termBuf[0] = (char)m_EOLTerminator[0];
	if (termLength > 1) termBuf[1] = (char)m_EOLTerminator[1];
	if (termLength > 2) termLength = 2;	// Force the issue

	result.Empty();

	char ch;
	unsigned long count;
	ReadFile(m_File, (void*)&ch, 1, &count, NULL);	// Rewrite this for speed!
	while (count == 1) {
		if ((termLength > 0) && (ch == termBuf[0])) {
			if (termLength == 1) return result.GetLength();
			char save = ch;
			ReadFile(m_File, (void*)&ch, 1, &count, NULL);
			if (count == 0) {
				result += save;
				return result.GetLength();
			}
			if (ch == termBuf[1]) return result.GetLength();
			result += save;
			result += ch;
		}
		else result += ch;
		ReadFile(m_File, (void*)&ch, 1, &count, NULL);
	}
	return result.GetLength();
}

int TFile::ReadUnicodeLine(TString& result)
{
	unsigned short termBuf[2];
	int termLength = m_EOLTerminator.GetLength();
	if (termLength > 0) termBuf[0] = m_EOLTerminator[0];
	if (termLength > 1) termBuf[1] = m_EOLTerminator[1];
	if (termLength > 2) termLength = 2;	// Force the issue

	result.Empty();

	unsigned short ch;
	unsigned long count;
	ReadFile(m_File, (void*)&ch, 2, &count, NULL);
	while (count == 2) {
		if ((termLength > 0) && (ch == termBuf[0])) {
			if (termLength == 1) return result.GetLength();
			char save = (char)ch;
			ReadFile(m_File, (void*)&ch, 2, &count, NULL);
			if (count == 0) {
				result += save;
				return result.GetLength();
			}
			if (ch == termBuf[1]) return result.GetLength();
			result += (TCHAR)save;
			result += (TCHAR)ch;
		}
		else result += (TCHAR)ch;
		ReadFile(m_File, (void*)&ch, 2, &count, NULL);
	}
	return result.GetLength();
}

////////////////////////////////////////////////////////
#else // !defined(__WINNT__) && !defined(__WINCE__)
// Not in Windows so compile TFile using stdio.h functions

#include <sys/stat.h>
#include <unistd.h>

// Values from stdio.h
int TFile::begin   = SEEK_SET;
int TFile::end     = SEEK_END;
int TFile::current = SEEK_CUR;

TFile::TFile():m_File(NULL), m_Filename(NULL){}

TFile::~TFile()
{
	Close(); // Close will delete m_Filename for us
}

void TFile::Save(LPCTSTR fileName, TString data)
{
	TFile file;
	if (!file.Open(fileName, TFile::write | TFile::create)) {
		return;
	}

#ifdef UNICODE
	int length = data.GetLength();
	char* buf = new char[length];
	for (int idx = 0; idx < length; ++idx) {
		buf[idx] = (char)(data[idx]);
	}
	file.Write(buf, length);
	delete buf;
#else
	file.Write((LPCTSTR)data, data.GetLength());
#endif
	file.Close();
}

TString TFile::Load(LPCTSTR fileName)
{
	TFile file;
	if (!TFile::Exists(fileName) || !file.Open(fileName, TFile::read)) {
		return _T("");
	}
	int length = file.GetLength();
	char* buf = new char[length + 1];
	file.Read(buf, length);
	buf[length] = 0;
	file.Close();

	TString result = buf;
	delete buf;

	return result;
}

bool TFile::DeleteFile(LPCTSTR filename)
{
	// This static function deletes the specified file
	// NOT this file, you should Close() the file before deleting it
	return _tremove(filename) == 0;
}

bool TFile::Move(LPCTSTR fileToMove, LPCTSTR newFileName)
{
	return _trename(fileToMove, newFileName) == 0;
}

bool TFile::Copy(LPCTSTR fileToCopy, LPCTSTR newFileName)
{
	ASSERT(false);
	return false;
}

bool TFile::MakeDir(LPCTSTR dirName)
{
	return mkdir(dirName,
		S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH) == 0;
}

bool TFile::RemoveDir(LPCTSTR dirName)
{
	return rmdir(dirName) == 0;
}

bool TFile::Exists(LPCTSTR filename)
{
	// Checks for files AND directories

	// Does stat work for directories?
	struct stat buf;
	if (stat(filename, &buf) == 0) {
		return true;
	}
	return false;
}

bool TFile::NewerThan(LPCTSTR first, LPCTSTR second)
{
	// Returns true is first was written to more recently than second
	struct stat f, s;
	if ((stat(first, &f) == 0) && (stat(second, &s) == 0)) {
		return f.st_mtime > s.st_mtime;
	}
	InternalError(_T("Compare file times failed, at least one file didn't exist"));
	return false;
}

bool TFile::Open(LPCTSTR lpszFileName, int nOpenMods)
{
	if (m_Filename != NULL) {
		// We need to close the current file before opening another
		Close();
	}

	char ReadWrite[3] = {0, 'b', 0};
	if (nOpenMods & read) ReadWrite[0] = 'r';
	else if (nOpenMods & write) ReadWrite[0] = 'w';
	else if (nOpenMods & append) {
		ReadWrite[0] = 'a';
		ReadWrite[1] = '+'
	}
	else {
		// We don't know if it's read of write!
		RunTimeError(_T("Read or write access not specified.  Can't open file: %s"), lpszFileName);
		m_File = NULL;
		return false;
	}

	//int CreateNew = nOpenMods & TFile::create ? CREATE_ALWAYS : OPEN_EXISTING;

	m_File = fopen(lpszFileName, ReadWrite);
	if (m_File == NULL) {
		// raise new exception
		RunTimeError(_T("Failed to open file: %s"), lpszFileName);
		m_File = NULL;
		return false;
	}

	int lenFilename = _tcslen(lpszFileName);
	m_Filename = new TCHAR[lenFilename + 1];
	_tcscpy(m_Filename, lpszFileName);

	if (!(nOpenMods & (unicode | ascii | text | binary))) {
		nOpenMods |= text;	// If not given, default to text file type
	}

	if (nOpenMods & unicode) {
		// Unicode designator
		m_FileType = unicode;
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & ascii) {
		// ASCII designator
		m_FileType = ascii;
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & text) {
		// Text designator (if reading, try and guess. If writing, use default for system)
		if (nOpenMods & 1) {
			// Reading file
			m_FileType = GuessTextFileType();
		}
		else {
			// Writing file
			#ifdef __WINCE__
				m_FileType = unicode;
			#else
				m_FileType = ascii;
			#endif
		}
		m_EOLTerminator = _T("\r\n");
	}
	else if (nOpenMods & binary) {
		// Binary mode
		m_FileType = binary;
		m_EOLTerminator.Empty();
	}

	return true;
}

void TFile::Close()
{
	if (m_File != NULL) {
		fclose(m_File);
		m_File = NULL;
	}
	if (m_Filename != NULL) {
		delete[] m_Filename;
		m_Filename = NULL;
	}
}

int TFile::GuessTextFileType()
{
	// Returns either unicode or ascii
	ASSERT(GetPosition() == 0);	// Only call when file is first opened

	if (GetLength() >= 2) {
		char buf[2] = {0, 0};
		Read(buf, 2);
		Seek(0, begin);
		if ((buf[0] == 0) || (buf[1] == 0)) return unicode;
	}
	return ascii;
}

unsigned long TFile::GetLength()
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		struct stat buf;
		if (fstat(fileno(m_File), &buf) == 0) {
			return buf.st_size;
		}
		return 0;
	}
}

unsigned long TFile::Seek(long offset, int from)
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		ASSERT((from == begin) || (from == current) || (from == end));
		return fseek(m_File, offset, from);
	}
}

unsigned long TFile::GetPosition()
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		return ftell(m_File);
	}
}

unsigned long TFile::Read(void* lpBuf, unsigned long nCount)
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		unsigned long nRead;
		nRead = fread(lpBuf, 1, nCount, m_File);
		return nRead;
	}
}

unsigned long TFile::Write(const void* lpBuf, unsigned long nCount)
{
	if (m_File == NULL) {
		return 0;
	}
	else {
		unsigned long nWritten;
		nWritten = fwrite(lpBuf, 1, nCount, m_File);
		return nWritten;
	}
}

void TFile::WriteLine(TString text)
{
	char* buf;
	int bufLength;
	if (m_FileType == unicode) {
		if (sizeof(TCHAR) == 2) {
			// No conversions needed
			Write((LPCTSTR)text, text.GetLength() * 2);
			WriteEOLTerminator();
			return;
		}
		bufLength = text.GetLength() * 2;
		buf = new char[bufLength];
		for (int idx = 0; idx < text.GetLength(); ++idx) {
			((unsigned short*)buf)[idx] = text[idx];
		}
	}
	else {
		if (sizeof(TCHAR) == 1) {
			// No conversions needed
			Write((LPCTSTR)text, text.GetLength());
			WriteEOLTerminator();
			return;
		}
		bufLength = text.GetLength();
		buf = new char[bufLength];
		for (int idx = 0; idx < text.GetLength(); ++idx) {
			((char*)buf)[idx] = (char)(text[idx]);
		}
	}

	Write(buf, bufLength);
	WriteEOLTerminator();
	delete[] buf;
}

void TFile::WriteEOLTerminator()
{
	char* buf;
	int length = m_EOLTerminator.GetLength();
	if (length > 0) {
		if (m_FileType == unicode) {
			buf = new char[length << 1];
			for (int idx = 0; idx < length; ++idx) {
				((unsigned short*)buf)[idx] = m_EOLTerminator[idx];
			}
			Write(buf, length << 1);
		}
		else {
			buf = new char[length];
			for (int idx = 0; idx < length; ++idx) {
				((char*)buf)[idx] = (char)(m_EOLTerminator[idx]);
			}
			Write(buf, length);
		}
	}
	delete[] buf;
}

int TFile::ReadLine(TString& result)
{
	if (m_FileType == unicode) {
		return ReadUnicodeLine(result);
	}
	else {
		return ReadASCIILine(result);
	}
}

bool TFile::SetEOLTerminator(TString term)
{
	if (term.GetLength() <= 2) {
		m_EOLTerminator = term;
		return true;
	}
	return false;
}

TString TFile::GetEOLTerminator()
{
	return m_EOLTerminator;
}

void TFile::Write(TString text)
{
	int length = text.GetLength();

	if (m_FileType == unicode) {
		if (sizeof(TCHAR) == 2) {
			Write((LPCTSTR)text, length << 1);
		}
		else {
			unsigned short* buf = new unsigned short[length];
			for (int idx = 0; idx < length; ++idx) {
				buf[idx] = text[idx];
			}
			Write(buf, length << 1);
			delete[] buf;
		}
	}
	else {
		if (sizeof(TCHAR) == 1) {
			Write((LPCTSTR)text, length);
		}
		else {
			char* buf = new char[length];
			for (int idx = 0; idx < length; ++idx) {
				buf[idx] = (char)(text[idx]);
			}
			Write(buf, length);
			delete[] buf;
		}
	}
}

int TFile::Read(TString& result, int numChars)
{
	if (m_File == NULL) return 0;

	DWORD bytesRead;
	if (m_FileType == unicode) {
		unsigned short* buf = new unsigned short[numChars + 1];
		bytesRead = fread((void*)buf, 1, numChars << 1, m_File);
		int charsRead = bytesRead == 0 ? 0 : bytesRead >> 1;
		buf[charsRead] = 0;
		result = (LPCWSTR)buf;
		delete[] buf;
		return charsRead;
	}
	else {
		char* buf = new char[numChars + 1];
		bytesRead = fread((void*)buf, 1, numChars, m_File);
		buf[bytesRead] = 0;
		result = buf;
		delete[] buf;
		return bytesRead;
	}
}

int TFile::ReadASCIILine(TString& result)
{
	char termBuf[2];
	int termLength = m_EOLTerminator.GetLength();
	if (termLength > 0) termBuf[0] = (char)m_EOLTerminator[0];
	if (termLength > 1) termBuf[1] = (char)m_EOLTerminator[1];
	if (termLength > 2) termLength = 2;	// Force the issue

	result.Empty();

	char ch;
	unsigned long count;
	count = fread(&ch, 1, 1, m_File);
	while (count == 1) {
		if ((termLength > 0) && (ch == termBuf[0])) {
			if (termLength == 1) return result.GetLength();
			char save = ch;
			count = fread(&ch, 1, 1, m_File);
			if (count == 0) {
				result += save;
				return result.GetLength();
			}
			if (ch == termBuf[1]) return result.GetLength();
			result += save;
			result += ch;
		}
		else result += ch;
		count = fread(&ch, 1, 1, m_File);
	}
	return result.GetLength();
}

int TFile::ReadUnicodeLine(TString& result)
{
	unsigned short termBuf[2];
	int termLength = m_EOLTerminator.GetLength();
	if (termLength > 0) termBuf[0] = m_EOLTerminator[0];
	if (termLength > 1) termBuf[1] = m_EOLTerminator[1];
	if (termLength > 2) termLength = 2;	// Force the issue

	result.Empty();

	unsigned short ch;
	unsigned long count;
	count = fread(&ch, 1, 2, m_File);
	while (count == 2) {
		if ((termLength > 0) && (ch == termBuf[0])) {
			if (termLength == 1) return result.GetLength();
			char save = (char)ch;
			count = fread(&ch, 1, 2, m_File);
			if (count == 0) {
				result += save;
				return result.GetLength();
			}
			if (ch == termBuf[1]) return result.GetLength();
			result += (TCHAR)save;
			result += (TCHAR)ch;
		}
		else result += (TCHAR)ch;
		count = fread(&ch, 1, 2, m_File);
	}
	return result.GetLength();
}

////////////////////////////////////////////////////////
#endif	// defined(__WINNT__) || defined(__WINCE__)

